#include "t_box_video.h"

namespace BoxPlayer //[class T_Box_Video_Multithread_Slice]
{
};

namespace BoxPlayer //[class T_Box_Video_Multithread_Manager]
{
    T_Box_Video_Multithread_Manager::T_Box_Video_Multithread_Manager(T_Box_Core *a_core)
        : T_Box_Context(a_core)
    {
        //this->openVideo();
        this->core()->seekManager()->addSeekable(this);
        for(int i=0; i<T_Box_Video_Multithread::NUM_THREAD; i++)
        {
            T_Box_Video_Multithread_Context *v_thread = new T_Box_Video_Multithread_Context(this, i);
            m_context_list.append(v_thread);
            v_thread->start();
        }
        this->start();
    }
    T_Box_Video_Multithread_Manager::~T_Box_Video_Multithread_Manager()
    {
        for(int i=0; i<m_context_list.size(); i++)
        {
            m_context_list.value(i)->finish();
            delete m_context_list.value(i);
        }
        this->finish();
    }
    qint64 T_Box_Video_Multithread_Manager::seekMe(T_Box_Seek_Manager *a_seek_mgr, qint64 a_msec)
    {
        qint64 v_seek_result = T_Box_Context::seekMe(a_seek_mgr, a_msec);
        this->reset();
        return v_seek_result;
    }
    void T_Box_Video_Multithread_Manager::run()
    {
        this->ready() = true;
        T_Box_Seek_Manager *v_seek_mgr = this->core()->seekManager();
        T_AutoRunClock v_clock;
        if(!this->openVideo())
        {
            return;
        }
        for(;;)
        {
            if(this->closing())
            {
                break;
            }
            v_seek_mgr->wait(1);
            if(v_clock.elapsed()>1000)
            {
                qDebug() << "[R:W](0)" <<  this->renderQueue()->size() << this->workerQueue()->size() << this->packetQueue()->size();
                v_clock.reset();
            }
            {
                T_READ_LOCKER(v_seek_lock, v_seek_mgr);
                //qDebug() << "[R:W](1)" <<  this->renderQueue()->size() << this->workerQueue()->size() << this->packetQueue()->size();
                while(!this->renderQueue()->isEmpty())
                {
                    QSharedPointer<T_Box_Video_Multithread_Slice> v_slice = this->renderQueue()->head();
                    while(!v_slice->isEmpty())
                    {
                        if(!v_slice->head()->isDecoded())
                        {
                            goto break1;
                        }
                        if(this->packetQueue()->size()>=(T_Box::PLAYER_IMAGE_STOCK*2)) goto break1;
                        this->packetQueue()->enqueue(v_slice->dequeue());
                        ////goto break1;//!!
                    }
                    if(!v_slice->f_complete()) goto break1;
                    this->renderQueue()->dequeue();//should render!
                }
                break1:
                //qDebug() << "[R:W](2)" <<  this->renderQueue()->size() << this->workerQueue()->size() << this->packetQueue()->size();
                if(this->renderQueue()->size()>T_Box_Video_Multithread::NUM_THREAD)
                {
                    //
                    v_seek_mgr->wait(10);
                }
                //else if(this->workerQueue()->size()<T_Box_Video_Multithread::NUM_THREAD)
                //else if(this->renderQueue()->size()+this->workerQueue()->size()<(T_Box_Video_Multithread::NUM_THREAD+1))
                else if(this->renderQueue()->size()<(T_Box_Video_Multithread::NUM_THREAD+1))
                {
                    if(this->totalQueue()->head())
                    {
                        QSharedPointer<T_Box_Video_Multithread_Slice> v_slice = this->totalQueue()->dequeue();
                        this->renderQueue()->enqueue(v_slice);
                        this->workerQueue()->enqueue(v_slice);
                    }
                }
                if(video()->maxTiming()>=v_seek_mgr->seekableDuration())
                {
                    continue;
                }
                QSharedPointer<T_AV_Packet> v_av_packet = this->enqueuePacket();
                if(!v_av_packet)
                {
                    continue;
                }
                Q_ASSERT(v_av_packet->media()->isVideo());
                if(v_av_packet->isKeyFrame())
                {
                    qDebug() << "<M>[v_av_packet->timing()]" << v_av_packet->timing();
                    T_Box_Video_Multithread_Slice *v_slice =
                            new T_Box_Video_Multithread_Slice(v_av_packet->timing());
                    this->totalQueue()->enqueue(v_slice);
                }
            }
        }
    }
};

namespace BoxPlayer //[class T_Box_Video_Multithread_Context]
{
    T_Box_Video_Multithread_Context::T_Box_Video_Multithread_Context(T_Box_Video_Multithread_Manager *a_manager, int a_index)
        : T_Box_Context(a_manager->core())
        , m_manager(a_manager)
        , m_index(a_index)
    {
        this->core()->seekManager()->addSeekable(this);
    }
    T_Box_Video_Multithread_Context::~T_Box_Video_Multithread_Context()
    {
        this->finish();
    }
    void T_Box_Video_Multithread_Context::reset()
    {
        T_ATOMIC_LOCKER(v_atom, this);
        //abandon decode slice here
        m_slice.clear();
    }
    qint64 T_Box_Video_Multithread_Context::seekMe(T_Box_Seek_Manager *a_seek_mgr, qint64 a_msec)
    {
        qint64 v_seek_result = T_Box_Context::seekMe(a_seek_mgr, a_msec);
        //abandon decode slice here
        this->reset();
        return v_seek_result;
    }
    QSharedPointer<T_Box_Video_Multithread_Slice> T_Box_Video_Multithread_Context::slice()
    {
        T_ATOMIC_LOCKER(v_atom, this);
        if(m_slice) return m_slice;
        if(m_slice = m_manager->workerQueue()->dequeue())
        {
            this->video()->flushBuffers();
        }
        return m_slice;
    }
    void T_Box_Video_Multithread_Context::run()
    {
        this->ready() = true;
        T_Box_Seek_Manager *v_seek_mgr = this->core()->seekManager();
        QSharedPointer<T_Box_Video_Multithread_Slice> v_slice;
        Q_ASSERT(!v_slice);
        if(!this->openVideo())
        {
            return;
        }
        for(;;)
        {
            if(this->closing())
            {
                break;
            }
            v_seek_mgr->wait(1);
            //qDebug() << m_index << "[v_slice->m_timing]" << v_slice->m_timing;
            {
                T_READ_LOCKER(v_seek_lock, v_seek_mgr);
                v_slice = this->slice();
                if(v_slice == NULL)
                {
                    continue;
                }
#if 0x1
                QSharedPointer<T_Box_Video_Multithread_Slice> v_render_head =
                        m_manager->renderQueue()->head();
                if(v_render_head && v_render_head->endlessCount() < v_slice->endlessCount())
                {
                    continue;
                }
#endif
                if(video()->maxTiming()>=v_seek_mgr->seekableDuration())
                {
                    continue;
                }
                QSharedPointer<T_AV_Packet> v_av_packet = this->headPacket();
                if(!v_av_packet)
                {
                    continue;
                }
                Q_ASSERT(v_av_packet->media()->isVideo());
                if(v_av_packet->timing()>v_slice->timing() && v_av_packet->isKeyFrame())
                {
                    qDebug() << m_index << "[before reset()]" << v_av_packet->frameNo();
                    v_slice->f_complete() = true;
                    this->reset();
                    qDebug() << m_index << "[after reset()]" << v_av_packet->frameNo();
                    continue;
                }
                v_av_packet = this->readPacket();
                if(v_av_packet->timing() < v_slice->timing())
                {
                    continue;
                }
                if(v_av_packet->timing() == v_slice->timing())
                {
                    Q_ASSERT(v_av_packet->isKeyFrame());
                }
#if 0x0
                //Q_ASSERT(!v_av_packet->isKeyFrame());
                //if(v_av_packet->isKeyFrame())
                qDebug()<< m_index << "[v_av_packet->timing()]"
                        << v_av_packet->frameNo()
                        << v_slice->timing()
                        << v_av_packet->timing()
                        << v_av_packet->isKeyFrame();
#endif
                v_av_packet->decodeVideoToNull(true);
                v_slice->enqueue(v_av_packet);
            }
        }
    }
};
